package hfu.pm;

import hfu.pm.bll.BusinessLogic;
import hfu.pm.bll.ChatLogic;
import hfu.pm.bll.ContactLogic;
import hfu.pm.entities.Account;
import hfu.pm.entities.Contact;
import hfu.pm.entities.Invitation;
import hfu.pm.entities.Message;
import hfu.pm.entities.Room;

import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.*;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.appengine.api.channel.ChannelMessage;
import com.google.appengine.api.channel.ChannelService;
import com.google.appengine.api.channel.ChannelServiceFactory;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.googlecode.objectify.Key;

@SuppressWarnings("serial")
public class ControllerServlet extends HttpServlet {

	public static String getUrl(HttpServletRequest req) {
		String reqUri = req.getRequestURI().toString();
		String queryString = req.getQueryString(); // d=789
		if (queryString != null) {
			reqUri += "?" + queryString;
		}
		return reqUri;
	}

	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws IOException {

		UserService userService = UserServiceFactory.getUserService();

		String action = req.getParameter("action");
		if (action == null)
			action = "";
		resp.setContentType("application/json");
		JSONObject json = new JSONObject();
		ChannelService channelService = ChannelServiceFactory
				.getChannelService();

		try {
			if (action.equals("checkLoginStatus")) {
				if (!userService.isUserLoggedIn()) {
					json.put("loggedin", false);
					json.put("loginURL",
							userService.createLoginURL("/index.jsp"));
				} else {
					BusinessLogic.registerAccount(userService.getCurrentUser());
					json.put("loggedin", true);
					json.put("user",
							new JSONObject(userService.getCurrentUser()));
					json.put("logoutURL",
							userService.createLogoutURL("/index.jsp"));
				}
			} else if (action.equals("getLoginURL")) {
				json.put("loginURL", userService.createLoginURL("/index.jsp"));
			} else if (action.equals("getLogoutURL")) {
				json.put("logoutURL", userService.createLogoutURL("/index.jsp"));
			} else if (action.equals("newFriend")) {
				if (!userService.isUserLoggedIn()) {
					String loginURL = userService.createLoginURL(getUrl(req));
					resp.sendRedirect(loginURL);
				} else {
					BusinessLogic.registerAccount(userService.getCurrentUser());

					String key = req.getParameter("key");

					Account currentAcc = BusinessLogic.getAccount(userService
							.getCurrentUser());
					Account friend = BusinessLogic.getAccount(key);
					if (friend != null) {
						ContactLogic.addToContactList(currentAcc, friend);
						// Send Channel Message to sender
						JSONObject jsonChannel = new JSONObject();
						jsonChannel.put("type", "gotFriend");

						channelService.sendMessage(new ChannelMessage(key,
								jsonChannel.toString()));
					}
					resp.sendRedirect("index.jsp");
				}
				// if action is not login, check for authentication
			} else if (userService.isUserLoggedIn()) {
				User currentUser = userService.getCurrentUser();
				Account currentAccount = BusinessLogic.getAccount(currentUser
						.getUserId());
				if (action.equals("getMessage")) {
					String roomKey = req.getParameter("roomKey");
					String from = req.getParameter("from");
					String last = req.getParameter("last");
					int x;

					try {
						x = Integer.parseInt(last);
					} catch (NumberFormatException e) {
						x = 50;
					}

					Room room = ChatLogic.getRoom(roomKey);

					List<Message> msgs = null;
					if (from != null) {
						SimpleDateFormat format = new SimpleDateFormat(
								"EEE MMM dd HH:mm:ss zzz yyyy");
						Date timestamp = format.parse(from);
						msgs = ChatLogic.getMessages(room, timestamp);
					} else {
						msgs = ChatLogic.getLastXMessages(room, x);
						Collections.reverse(msgs);
					}
					JSONArray jsonMessages = new JSONArray();
					for (Message msg : msgs) {
						Account sender = (BusinessLogic.get(msg.getSender()));
						Contact contact = ContactLogic.getContactOf(
								currentAccount, sender);
						jsonMessages.put(new JSONObject(msg).put("sender",
								contact.getFriendName()));
					}
					json.put("messages", jsonMessages);
				} else if (action.equals("sendMessage")) {
					String roomKey = req.getParameter("roomKey");
					String message = req.getParameter("message");
					try {
						Room room = ChatLogic.getRoom(roomKey);
						ChatLogic.sendMessage(currentAccount, room, message);
						json.put("status", "ok");

						// Send Channel Message to room's subscriber
						List<Account> accs = ChatLogic.getSubscribedUsers(room);
						JSONObject jsonChannel = new JSONObject();
						jsonChannel.put("type", "gotMessage");
						jsonChannel.put("roomKey", new Key<Room>(Room.class,
								room.getId()).getString());
						if (!room.isPrivate()) 
							jsonChannel.put("roomHeader", room.getName());
						
						for (Account account : accs) {
							if (!account.getId().equals(currentAccount.getId())) {
								String key = (new Key<Account>(Account.class,
										account.getId())).getString();
								String contactName = ContactLogic.getContactOf(account, currentAccount).getFriendName();
								
								if (room.isPrivate())
									jsonChannel.put("roomHeader", contactName);
								channelService.sendMessage(new ChannelMessage(
										key, jsonChannel.toString()));
							}
						}

					} catch (Exception e) {
						json.put("status", "error");
						json.put("error", e.getMessage());
					}

				} else if (action.equals("getRoomList")) {
					List<Room> rooms = ChatLogic
							.getSubscribedPublicRoomsOf(currentAccount);
					JSONArray jsonRooms = new JSONArray();
					for (Room room : rooms) {
						String k = new Key<Room>(Room.class, room.getId())
								.getString();
						jsonRooms.put(new JSONObject(room).put("key", k));
					}
					json.put("rooms", jsonRooms);
					// ContactLogic.addToContactList(currentAccount,
					// BusinessLogic.getAccount("19914962852421917436"));
					// ChatLogic.addNewUserToRoom(ChatLogic.getRoom("ag5wb2tlLW1lc3NlbmdlcnIKCxIEUm9vbRhCDA"),
					// BusinessLogic.getAccount("19914962852421917436"));
				} else if (action.equals("getCurrentUser")) {
					json.put("user",
							new JSONObject(userService.getCurrentUser()));
				} else if (action.equals("createRoom")) {
					String roomName = req.getParameter("roomName");
					if (roomName == null) {
						json.put("status", "error");
						JSONObject error = new JSONObject();
						error.put("code", 2);
						error.put("description", "Room's name is required");

						json.put("error", error);
					} else {
						Room newRoom = ChatLogic.createPublicChatRoom(
								currentAccount, roomName);
						json.put("status", "ok");
						json.put("room", new JSONObject(newRoom));
					}
				} else if (action.equals("getChannelToken")) {

					String accKey = (new Key<Account>(Account.class,
							currentAccount.getId())).getString();
					json.put("token", channelService.createChannel(accKey));
				} else if (action.equals("inviteFriend")) {
					String url = req.getServerName() + ":"
							+ req.getServerPort()
							+ "/service?action=newFriend&key="
							+ currentAccount.getGoogleUserId();

					String email = req.getParameter("email");
					if (email == null || email.trim().length() == 0) {
						json.put("status", "error");
						JSONObject error = new JSONObject();
						error.put("code", 2);
						error.put("description", "Friend's email is required");

						json.put("error", error);
					} else {
						int tmp = BusinessLogic.sendInvitationEmail(
								currentAccount, email, url);

						if (tmp == 0)
							json.put("status", "ok");
						else {
							json.put("status", "error");
							JSONObject error = new JSONObject();
							error.put("code", 2);
							if (tmp == 1)
								error.put("description",
										"This email's already registered");
							else
								error.put("description",
										"Error occured when was sending invitation email");
						}
					}

				} else if (action.equals("invite")) {
					String contactKey = req.getParameter("friend");
					String roomKey = req.getParameter("room");

					Account friend = ContactLogic
							.getFriendOfContact(ContactLogic
									.getContact(contactKey));
					Room room = ChatLogic.getRoom(roomKey);

					try {
						ChatLogic.inviteToRoom(room, currentAccount, friend);
						json.put("status", "ok");
					} catch (Exception e) {
						json.put("status", "error");
						json.put("error", e.getMessage());
					}

					String accKey = (new Key<Account>(Account.class,
							friend.getId())).getString();
					JSONObject channelMessage = new JSONObject();
					channelMessage.put("type", "gotInvitation");
					channelService.sendMessage(new ChannelMessage(accKey,
							channelMessage.toString()));
				} else if (action.equals("invitationList")) {
					List<Invitation> invitations = ChatLogic
							.getInvitationList(currentAccount);
					JSONArray jsonInvitations = new JSONArray();
					for (Invitation invi : invitations) {
						String k = new Key<Invitation>(Invitation.class,
								invi.getId()).getString();
						Account sender = BusinessLogic.get(invi.getSender());
						Room r = BusinessLogic.get(invi.getRoom());

						String senderName = ContactLogic.getContactOf(
								currentAccount, sender).getFriendName();
						String roomName = r.getName();

						jsonInvitations.put(new JSONObject(invi).put("key", k)
								.put("sender", senderName)
								.put("room", roomName));
					}
					json.put("invitations", jsonInvitations);

				} else if (action.equals("reply")) {
					String invKey = req.getParameter("invitation");
					String accept = req.getParameter("accept");
					Invitation inv = ChatLogic.getInvitation(invKey);

					if (accept.equals("no")) {
						ChatLogic.respondToARoomInvitation(inv, false);
					} else {
						ChatLogic.respondToARoomInvitation(inv, true);
					}
				} else if (action.equals("getContactList")) {
					List<Contact> contacts = ContactLogic
							.getContactList(currentAccount);
					JSONArray jsonContacts = new JSONArray();
					for (Contact contact : contacts) {
						String k = new Key<Contact>(Contact.class,
								contact.getId()).getString();
						JSONObject tmp = new JSONObject(contact);
						tmp.put("key", k);

						Account friend = ContactLogic.getAccount(contact
								.getFriend().getString());
						Room room = ChatLogic.getPrivateChatRoom(
								currentAccount, friend);
						String roomKey = new Key<Room>(Room.class, room.getId())
								.getString();
						tmp.put("roomKey", roomKey);

						jsonContacts.put(tmp);
					}
					json.put("contacts", jsonContacts);
				}

			} else {
				json.put("status", "error");

				JSONObject error = new JSONObject();
				error.put("code", 1);
				error.put("description", "Must login to use this function");
				json.put("error", error);
			}
			resp.getWriter().write(json.toString());

		} catch (JSONException e) {
			Logger logger = Logger.getAnonymousLogger();
			logger.log(Level.SEVERE, e.getMessage(), e.getStackTrace());
		} catch (ParseException e) {
			Logger logger = Logger.getAnonymousLogger();
			logger.log(Level.SEVERE, e.getMessage(), e.getStackTrace());
		}
	}

	@Override
	public void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

	}
}
